#pragma once

//Engine
#include "Common.h"
#include "NeroEngine\NPlatform.h"
#include "NeroEngine\NDevice.h"

#ifdef DX_11
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "d3dx11.lib")
#pragma comment(lib, "d3dx10.lib")
//DX11
#include <dxgi.h>
#include <d3dcommon.h>
#include <d3d11.h>
#include "D3DX10math.h"

#define CHECK(result)   if(!Graphics::D3DDevice::CheckResult(result))		\
						{													\
							return false;									\
						}

const bool FULL_SCREEN		= false;
const bool VSYNC_ENABLED	= true;
const float SCREEN_DEPTH	= 100.0f;
const float SCREEN_NEAR		= 1.0f;

namespace Graphics
{
struct GraphicsSettings
{			
	bool VsyncEnabled;
	int  ShowCommand;								//!<
	bool DepthBufferEnabled;						//!< is the depth buffer enabaled
};
struct WindowsSettings
{
#ifdef WIN32
	D3D_FEATURE_LEVEL		FeatureLevel;
	HDC						HDC;
	HINSTANCE				Instance;
	HWND					WindowHandle;
	const char*				WindowTitle;				//!< the title name of the window
	const char*				WindowClass;				//!< the class name of the window
#endif
};

struct RenderState
{
	ID3D11DepthStencilState*	DepthStencilState;
	ID3D11DepthStencilState*	DepthDisabledStencilState;
	ID3D11BlendState*			AlphaEnableBlendingState;
	ID3D11BlendState*			AlphaDisableBlendingState;
	ID3D11RasterizerState*		RasterizerState;
};
struct Resolution
{
	int ScreenHeight;								//!< the screen height
	int ScreenWidth;								//!< the screen width
	int ScreenNumerator;
	int ScreenDenominator;
	int VideoCardMemory;
	char VideoCardDescription[128];
};

class D3DDevice : public Engine::NDevice
{
private:
	RTTI_DECLARATIONS(D3DDevice, Engine::NDevice)
public:
	D3DDevice(Engine::NPlatform* owner);
	virtual ~D3DDevice(void);
	void Initialize(HINSTANCE instance, int showCommand);
	virtual void Deinitialize();

	ID3D11Device* GetDevice();
	ID3D11DeviceContext* GetDeviceContext();
	IDXGISwapChain* GetSwapChain();
	ID3D11DepthStencilView* GetDepthStencilView();

	void SetBackBufferRenderTarget();

	Resolution GetResolutionInfo();
	WindowsSettings GetWindowsSettings();

	bool IsVsyncEnabled();

	void BeginRender();
	void EndRender();

	D3DMATRIX GetViewMatrix();
	D3DMATRIX GetWorldMatrix();
	D3DMATRIX GetProjectionMatrix();
	D3DMATRIX GetOrthoMatrix();

	void ToggleFullScreen(){};

	void TurnOnAlphaBlending();
	void TurnOffAlphaBlending();

	void TurnZBufferOn();
	void TurnZBufferOff();

	LRESULT CALLBACK MessageHandler(HWND, UINT, WPARAM, LPARAM);
private:
	void InitWindowHandle();
	void InitD3DDevice();

	bool SetupGraphicCardInfo();
	bool SetupSwapChain();
	bool SetupBackBuffer();
	bool SetupDepthStencil();
	bool SetupRasterizer();
	bool SetupAlphaBlend();

private:
	ID3D11Device* mDevice;
	ID3D11DeviceContext* mDeviceContext;
	IDXGISwapChain* mSwapChain;
	ID3D11RenderTargetView* mRenderTargetView;
	ID3D11DepthStencilView*  mDepthStencilView;
	ID3D11Texture2D* mDepthStencilBuffer;
	Resolution mResolution;
	RenderState mRenderState;
	GraphicsSettings mGraphicsSettings;
#ifdef WIN32
	WindowsSettings mWindowsSettings;
#endif
	bool mIsFullscreen;
	D3DXMATRIX mViewMatrix;
	D3DXMATRIX mProjectionMatrix;
	D3DXMATRIX mWorldMatrix;
	D3DXMATRIX mOrthoMatrix;
#ifdef _DEBUG
	ID3D11Debug* mD3D_Debug;
#endif
	
public:
	static LRESULT CALLBACK WndProc(HWND windowHandle, UINT message, WPARAM wParam,LPARAM lParam);
	static bool CheckResult(HRESULT result);
};
}
#elif defined( DX_9 )
#define D3D_DEBUG_INFO

#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9d.lib")
#pragma comment(lib, "dxguid.lib")

#define CUSTOMFVF (D3DFVF_XYZ | D3DFVF_DIFFUSE)

#define CHECK(result) D3DDevice::CheckResult(result);

#include <d3d9.h>
#include <d3dx9.h>
#include "D3DX10math.h"

namespace Graphics
{

struct GraphicsSettings
{			
	bool VsyncEnabled;								//!<
	int  ShowCommand;								//!<
	bool DepthBufferEnabled;						//!< is the depth buffer enabaled
};

struct WindowsSettings
{
	HDC						HDC;					//!<
	HINSTANCE				Instance;				//!<
	HWND					WindowHandle;			//!<
	const char*				WindowTitle;			//!< the title name of the window
	const char*				WindowClass;			//!< the class name of the window
};

struct Resolution
{
	int ScreenHeight;								//!< the screen height
	int ScreenWidth;								//!< the screen width
	int ScreenNumerator;							//!<
	int ScreenDenominator;							//!<
	int VideoCardMemory;							//!<
	char VideoCardDescription[128];					//!<
};

class D3DDevice : public Engine::NDevice
{
	RTTI_DECLARATIONS(D3DDevice, Engine::NDevice)

public:
	D3DDevice(Engine::NPlatform* owner, HINSTANCE instance, int showCommand);

	virtual ~D3DDevice(void);

	virtual void Initialize();

	virtual void Deinitialize();

	void InitWindowHandle();

	void InitD3DDevice();

	void BeginRender();

	void EndRender();

	LPDIRECT3DDEVICE9 GetDevice();

	Resolution GetResolutionInfo();

	WindowsSettings GetWindowsSettings();

private:
	LPDIRECT3DDEVICE9 mDevice;

	LPDIRECT3D9 m3DDevice;
	
	WindowsSettings mWindowsSettings;

	Resolution mResolution;

	GraphicsSettings mGraphicsSettings;

public:
	static LRESULT CALLBACK WndProc(HWND windowHandle, UINT message, WPARAM wParam,LPARAM lParam);
	static void CheckResult(HRESULT result);
};
}
#endif